/**
 * <copyright>
 * 
 * Copyright (c) 2008 IBM Corporation and others.
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-v2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 * 
 * Contributors:
 * IBM - Initial API and implementation
 * 
 * </copyright>
 * 
 * $Id: ChildCreationExtenderManager.java,v 1.1 2008/01/29 21:13:13 emerks Exp $
 */
package org.eclipse.egf.model.fcore.provider;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.DelegatingResourceLocator;
import org.eclipse.emf.common.util.ResourceLocator;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.provider.IChildCreationExtender;

/**
 * A child creation extension manager encapsulates a {@link #getChildCreationExtenders() list of child creation extenders} for a particular package.
 * The list is automatically populated based on {@link IChildCreationExtender.Descriptor.Registry#getDescriptors(String) registered extensions} for the package.
 * The manager also acts as resource locator that loads resources from either the primary resource locator or one of the extenders.
 */
public class ResourceChildCreationExtenderManager extends DelegatingResourceLocator {

  protected Collection<EClass> roots;
  /**
   * The primary resource locator.
   */
  protected ResourceLocator primaryResourceLocator;

  /**
   * A key in the {@link IChildCreationExtender.Descriptor.Registry child creation extender registry} which will typically be the namespace of the package being extended.
   */
  protected String namespace;

  /**
   * A specialized child creation extender list implementation that provides efficient access to the resource locators of the extenders.
   */
  private static class ChildCreationExtenderList extends BasicEList<IChildCreationExtender> {

    private static final long serialVersionUID = 1L;

    /**
     * An empty array.
     */
    private static final ResourceLocator[] NO_RESOURCE_LOCATORS = new ResourceLocator[0];

    /**
     * A cache of the resource locators of the extenders.
     */
    protected ResourceLocator[] delegateResourceLocators = NO_RESOURCE_LOCATORS;

    /**
     * The modCount the last time the delegateResourceLocators were computed.
     */
    protected int expectedModCount = modCount;

    /**
     * Creates an empty new instance.
     */
    public ChildCreationExtenderList() {
      super();
    }

    /**
     * This list may not contain null.
     */
    @Override
    protected boolean canContainNull() {
      return false;
    }

    /**
     * This list may only contain instances of {@link IChildCreationExtender}.
     */
    @Override
    protected Object[] newData(int capacity) {
      return new IChildCreationExtender[capacity];
    }

    /**
     * Returns an efficiently computed array of the delegate resource locators.
     */
    protected ResourceLocator[] getDelegateResourceLocators() {
      if (expectedModCount != modCount) {
        expectedModCount = modCount;
        if (size == 0) {
          delegateResourceLocators = NO_RESOURCE_LOCATORS;
        } else {
          IChildCreationExtender[] childCreationExtenders = (IChildCreationExtender[]) data;
          delegateResourceLocators = new ResourceLocator[size];
          for (int i = 0; i < size; ++i) {
            delegateResourceLocators[i] = childCreationExtenders[i].getResourceLocator();
          }
        }
      }
      return delegateResourceLocators;
    }
  }

  /**
   * The list of child creation extenders.
   */
  protected ChildCreationExtenderList childCreationExtenders;

  /**
   * Creates an instance for the given primary resource locator to manage the extensions for the given namespace.
   * 
   * @param primaryResourceLocator
   *          the resource locator for finding basic resources.
   * @param namespace
   *          a key in the {@link IChildCreationExtender.Descriptor.Registry child creation extender registry} which will typically be the namespace of the package whose extensions are being managed.
   */
  public ResourceChildCreationExtenderManager(ResourceLocator primaryResourceLocator, String namespace) {
    this.primaryResourceLocator = primaryResourceLocator;
    this.namespace = namespace;
  }

  @Override
  protected ResourceLocator[] getDelegateResourceLocators() {
    getChildCreationExtenders();
    return childCreationExtenders.getDelegateResourceLocators();
  }

  @Override
  protected ResourceLocator getPrimaryResourceLocator() {
    return primaryResourceLocator;
  }

  /**
   * Returns a modifiable list of the child creation extenders.
   * 
   * @return a modifiable list of the child creation extenders.
   */
  public List<IChildCreationExtender> getChildCreationExtenders() {
    if (childCreationExtenders == null) {
      childCreationExtenders = new ChildCreationExtenderList();
      for (IChildCreationExtender.Descriptor descriptor : IChildCreationExtender.Descriptor.Registry.INSTANCE.getDescriptors(namespace)) {
        childCreationExtenders.add(descriptor.createChildCreationExtender());
      }
    }
    return childCreationExtenders;
  }

  /**
   * Returns the list of new child descriptors accumulated from each of the child creation extenders.
   * 
   * @param object
   *          the owner object of the descriptors.
   * @param editingDomain
   *          the domain for the descriptors.
   * @return the list of new child descriptors accumulated from each of the child creation extenders.
   */
  public List<?> getNewChildDescriptors(Object object, EditingDomain editingDomain) {
    List<Object> result = new ArrayList<Object>();
    for (IChildCreationExtender childCreationExtender : getChildCreationExtenders()) {
      result.addAll(childCreationExtender.getNewChildDescriptors(object, editingDomain));
    }
    return result;
  }

  public Collection<EClass> getRoots() {
    if (roots == null) {
      roots = new UniqueEList<EClass>();
      for (IChildCreationExtender childCreationExtender : getChildCreationExtenders()) {
        if (childCreationExtender instanceof IResourceChildCreationExtender) {
          roots.addAll(((IResourceChildCreationExtender) childCreationExtender).getRoots());
        }
      }
    }
    return roots;
  }

}
